﻿Partial Class Report

	Public Class Table

		Public Rows As New List(Of Row)
		Public Columns As New List(Of Column)

		Sub New(ByVal Columns As String)
			AddColumns(Columns)
		End Sub

		Property HasBorders As Boolean

		Sub AddColumns(ByVal Columns As String)
			For Each Item In Columns.Split(";")
				Me.Columns.Add(New Column(Item))
				RowSpanInfo.Add(0)
			Next
		End Sub

		Dim RowSpanInfo As New List(Of Integer)

		<DebuggerHidden()>
		Function AddRow(ByVal ParamArray Values() As Object) As Row
			Rows.Add(New Row(Me, RowSpanInfo, Values))
			Return Rows.Last
		End Function

		Function AddHeader(ByVal ParamArray Values() As Object) As Row
			Rows.Add(New Row(Me, RowSpanInfo, Values))
			Rows.Last.Header = True
			Return Rows.Last
		End Function

		Sub Prepare(ByVal e As Layout)
			Dim Total As Integer = (From c In Columns Select c.Width).Sum()
			For Each c In Columns
				c.ActualWidth = e.Width * c.Width \ Total
			Next
			Dim AWSum = Columns.Sum(Function(c) c.ActualWidth)
			Dim MaxCol = Columns.OrderBy(Function(c) c.ActualWidth).Last
			MaxCol.ActualWidth += e.Width - AWSum
			Dim x As Integer = 0
			For Each c In Columns
				c.ActualX = x
				x += c.ActualWidth
			Next
			For Each Row In Rows
				Row.Prepare(e)
			Next
			For Each d In (From r In Rows From c In r.Cells Where c.Rowspan > 1)
				Dim ri = Rows.IndexOf(d.r)
				For i = 2 To d.c.Rowspan
					For Each Item In d.c.Items
						Item.Rect.Height += Rows(i - 1).RowHeight
					Next
				Next
			Next
		End Sub

		Sub PrepareHeaders(ByVal e As Layout)
			For Each Row In Rows.Where(Function(r) r.Header)
				Row.Prepare(e)
			Next
		End Sub

		Sub Prepare(ByVal Builder As StringBuilder)
			Builder.Append("<table width=100% border=1 cellpadding=4>")
			For Each Row In Rows
				Row.Prepare(Builder)
			Next
			Builder.Append("</table>")
		End Sub

		Function GetWidth(ByVal Column As Column, ByVal Colspan As Integer) As Integer
			Dim ci = Columns.IndexOf(Column)
			Return (From c In Columns Where Columns.IndexOf(c) - ci >= 0 And Columns.IndexOf(c) - ci < Colspan Select c.ActualWidth).Sum
		End Function

	End Class

	Public Class Row

		Dim Table As Table
		Public Cells As New List(Of Cell)
		Public Header As Boolean

		<DebuggerHidden()>
		Sub New(ByVal Table As Table, ByVal RowSpanInfo As List(Of Integer), ByVal ParamArray Values() As Object)
			Me.Table = Table
			Dim i As Integer = 0
			For Each Value As Object In Values
				Do
					If RowSpanInfo(i) = 0 Then Exit Do
					i += 1
				Loop
				Cells.Add(New Cell(Value, Table.HasBorders))
				Cells.Last.Column = Table.Columns(i)
				If Cells.Last.Rowspan > 0 Then
					For j = i To i + Cells.Last.Colspan - 1
						RowSpanInfo(j) = Cells.Last.Rowspan
					Next
				End If
				i += Cells.Last.Colspan
			Next
			For i = 0 To RowSpanInfo.Count - 1
				If RowSpanInfo(i) > 0 Then RowSpanInfo(i) -= 1
			Next i
		End Sub

		Public RowHeight As Integer

		Sub Prepare(ByVal e As Layout)
			Do
				For Each Cell In Cells
					Cell.Rect = New System.Drawing.Rectangle(Cell.Column.ActualX, e.y, Table.GetWidth(Cell.Column, Cell.Colspan), 0)
					Cell.Measure(e)
				Next
				RowHeight = (From c In Cells Where c.Rowspan = 1 Select c.Rect.Height).Max
				If e.CheckPageLimits(RowHeight) Then Exit Do
				Table.PrepareHeaders(e)
			Loop
			For Each Cell In Cells
				Cell.Rect.Height = RowHeight
				Cell.Prepare(e)
			Next
			e.y += RowHeight
		End Sub

		Sub Prepare(ByVal Builder As StringBuilder)
			Builder.Append("<tr>")
			For Each Cell In Cells
				Cell.Prepare(Builder)
			Next
			Builder.Append("</tr>")
		End Sub

	End Class

	Public Class Column

		Public Width As Integer
		Public Type As String
		Public Align As String = "center"
		Public ActualWidth As Integer
		Public ActualX As Integer

		Sub New(ByVal Definition As String)
			Do
				If Definition = "" Then Exit Do
				Dim c = Definition.Last
				If Asc(c) >= 0 And Asc(c) <= 9 Then Exit Do
				Select Case c
					Case "n" : Type = "number" : Align = "right"
					Case "l" : Align = "left"
					Case "c" : Align = "center"
					Case "r" : Align = "right"
					Case "d" : Type = "date" : Align = "center"
					Case Else : Exit Do
				End Select
				Definition = Mid(Definition, 1, Definition.Length - 1)
			Loop
			Width = Definition
		End Sub

	End Class

	Public Class Cell

		Public Value As Object
		Public Colspan As Integer = 1
		Public Rowspan As Integer = 1
		Public Column As Column
		Public Rect As Rectangle
		Public Items As New List(Of Layout.PrintItem)
		Public Bold As Boolean
		Public HasBorders As Boolean

		Sub New(ByVal Value As Object, HasBorders As Boolean)
			Me.Value = Value
			Dim Text = CStr(Value)
			Do
				If Text.StartsWith("<cs=") Then
					Text = Mid(Text, 5)
					Dim Pos = InStr(Text, ">")
					Colspan = Mid(Text, 1, Pos - 1)
					Text = Mid(Text, Pos + 1)
				ElseIf Text.StartsWith("<rs=") Then
					Text = Mid(Text, 5)
					Dim Pos = InStr(Text, ">")
					Rowspan = Mid(Text, 1, Pos - 1)
					Text = Mid(Text, Pos + 1)
				ElseIf Text.StartsWith("<c>") Then
					Align = "center"
					Text = Mid(Text, 4)
				ElseIf Text.StartsWith("<r>") Then
					Align = "right"
					Text = Mid(Text, 4)
				ElseIf Text.StartsWith("<b>") Then
					Bold = True
					Text = Mid(Text, 4)
				Else
					Exit Do
				End If
			Loop
			Me.Value = Text
			Me.HasBorders = HasBorders
		End Sub

		Dim _Align As String = ""
		Property Align As String
			Get
				Return If(_Align = "", Column.Align, _Align)
			End Get
			Set(ByVal value As String)
				_Align = value
			End Set
		End Property

		Sub Prepare(ByVal Builder As StringBuilder)
			Builder.Append("<td")
			If Colspan > 1 Then Builder.Append(" colspan=" & Colspan)
			If Rowspan > 1 Then Builder.Append(" rowspan=" & Rowspan)
			If Align <> "" Then Builder.Append(" align=" & Align)
			Builder.Append(">")
			Builder.Append(Value)
			Builder.Append("</td>")
		End Sub

		Sub Measure(ByVal e As Layout)
			Rect.Height = e.Graphics.MeasureString(Value, Font, Rect.Width).Height * 1.4
		End Sub

		Sub Prepare(ByVal e As Layout)
			Dim Item As New Layout.PrintItem
			Item.Rect = Rect
			Item.Text = Value
			Item.Font = Font
			Item.Align = Align
			Item.HasBorders = HasBorders
			e.AddItem(Item)
			Items.Add(Item)
		End Sub

		ReadOnly Property Font As Font
			Get
				Dim FontName = "segoe ui"
				Dim FontSize = 9
				Dim FontStyle As FontStyle
				If Bold Then FontStyle += Drawing.FontStyle.Bold
				Return New Font(FontName, FontSize, FontStyle)
			End Get
		End Property

	End Class

End Class
